//运行服务
#pragma once

#include<iostream>
#include<string>
#include<unistd.h>
#include"../comm/util.hpp"
#include"../comm/log.hpp"
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<sys/wait.h>
#include<sys/time.h>
#include<sys/resource.h>

namespace ns_runner
{
    using namespace ns_util;
    using namespace ns_log;
    class Runner
    {
    public:
        Runner()
        {}
        ~Runner()
        {}
    public:

        //提供设置进程占用资源大小的接口
        static void SetProcLimit(int _cpu_limit,int _mem_limit)
        {
            struct rlimit cpu_limit; // Linux资源限制结构体（存CPU限制）
            cpu_limit.rlim_cur=_cpu_limit;// 软限制（当前限制，比如1秒）
            cpu_limit.rlim_max=RLIM_INFINITY;// 硬限制（最大限制，无限）
            setrlimit(RLIMIT_CPU,&cpu_limit);

            struct rlimit mem_limit;
            mem_limit.rlim_cur=_mem_limit*1024;// 转成KB（比如用户传10MB，就是10*1024 KB）
            mem_limit.rlim_max=RLIM_INFINITY;
            // 系统调用：设置内存限制（RLIMIT_AS：虚拟内存限制，包括代码、数据等）
            setrlimit(RLIMIT_AS,&mem_limit);
        }
        //指明文件名即可，不需要带路径，不需要带后缀

        /************************************
        *返回值>0:程序异常了，退出时收到了信号，返回值就是收到的信号
        *返回值==0:程序正常运行完毕，结果保存到对应的临时文件中
        *返回值<0:内部错误
        *
        *cpu_limit:该程序运行的时候，可以使用的最大cpu资源上限
        *mem_limit:该程序运行的时候，可以使用的最大内存大小（KB）
        *************************************/

        static int Run(const std::string& file_name,int cpu_limit,int mem_limit)
        {
            //程序运行应该考虑：是否正确运行完毕
            /**************************************
            * 1.代码跑完，结果正确
            * 2.代码跑完，结果不正确
            * 3.代码没跑完，异常退出
            *
            *一个程序默认启动时：
            * 1.标准输入：
            * 2.标准输出：程序运行完成的输出结果
            * 3.标准错误：程序运行时的错误信息
            ***************************************/
            std::string _execute=PathUtil::Exe(file_name);//带路径带后缀的可执行程序
            std::string _stdin=PathUtil::Stdin(file_name);
            std::string _stdout=PathUtil::Stdout(file_name);
            std::string _stderr=PathUtil::Stderr(file_name);

            umask(0);
            int _stdin_fd=open(_stdin.c_str(),O_CREAT|O_RDONLY,0644);
            int _stdout_fd=open(_stdout.c_str(),O_CREAT|O_WRONLY,0644);
            int _stderr_fd=open(_stderr.c_str(),O_CREAT|O_WRONLY,0644);

            if(_stderr_fd<0||_stdout_fd<0||_stderr_fd<0)
            {
                LOG(ERROR)<<"运行时打开标准文件失败 "<<"\n";
                return -1;//代表打开文件失败
            }

            pid_t pid=fork();
            if(pid<0)
            {
                LOG(ERROR)<<"运行时创建子进程失败"<<"\n";
                close(_stdin_fd);
                close(_stdout_fd);
                close(_stderr_fd);
                return -2;//代表创建子进程失败
            }
            else if(pid==0)
            {
                //子进程
                dup2(_stdin_fd,0);
                dup2(_stdout_fd,1);
                dup2(_stderr_fd,2);

                SetProcLimit(cpu_limit,mem_limit);

                //可以带全路径执行例如下面
                //execl("./temp/123.exe", "./temp/123.exe", nullptr)
                execl(_execute.c_str(),_execute.c_str()/*在命令行上可以全路径执行*/,nullptr);
                exit(1);
            }
            else
            {
                close(_stdin_fd);
                close(_stdout_fd);
                close(_stderr_fd);

                int status=0;
                waitpid(pid,&status,0);
                //程序运行异常---收到了信号
                // 解析子进程退出状态：status&0x7F是子进程收到的信号（比如超时14、内存超13）
                LOG(INFO)<<"运行完毕,info:"<<(status & 0x7F)<<"\n";
                return (status & 0x7F);// 返回信号值（>0=异常，0=正常）
            }
        }
    };
}